#include "ipcdevice.h"
#include "timer.h"

#include <fstream>

#define SHARED_BUFFER_SIZE				4096
#define SHARED_HEADER_SIZE				sizeof(long)
#define MAPPING_PREFIX					"MM_MEMORY_"
#define MUTEX_PREFIX					"MM_MUTEX_"

#define OPEN_TIMEOUT					5000

IpcDevice::IpcDevice()
{
	lpMem = NULL;
	hMapObject = NULL;
	memMutex = NULL;
	//memEvent = NULL;
	opened = false;
}

IpcDevice::~IpcDevice()
{

}

/* Open a shared memory connection
	to the injected process */
int IpcDevice::open(DWORD procId)
{
	hMapObject = NULL;
	lpMem = NULL;
	memMutex = NULL;
	opened = false;

	/* Construct object names based on
		process ID */
	char itoabuff[33];
	itoa((int)procId, itoabuff, 10);

	char shared_name[128];
	strcpy(shared_name, MAPPING_PREFIX);
	strcat(shared_name, itoabuff);

	char mutex_name[128];
	strcpy(mutex_name, MUTEX_PREFIX);
	strcat(mutex_name, itoabuff);


	/* Try connecting to the opposite end */
	TimeType startTime = getNow();

	while( hMapObject == NULL )
	{
		hMapObject = OpenFileMapping(FILE_MAP_WRITE, false, shared_name);

		if( !hMapObject && deltaTime(getNow(), startTime)*1000 > OPEN_TIMEOUT )
			break;
	}

	/* If failed to connect within time limit, return false */
	if( !hMapObject )
		return false;


	/* Get a pointer to the shared memory */
	lpMem = MapViewOfFile(hMapObject, FILE_MAP_WRITE, 0, 0, 0);
	if( lpMem == NULL )
		return false;

	/* Open a mutex... */
	memMutex = CreateMutex(NULL, false, mutex_name);
	if( memMutex == NULL )
		return false;


	/* Everything is set up! */
	opened = true;
	return true;
}

int IpcDevice::close()
{
	if( opened )
	{
        ReleaseMutex(memMutex);

		CloseHandle(memMutex);
		CloseHandle(hMapObject);

		hMapObject = NULL;
		memMutex = NULL;
		lpMem = NULL;
		opened = false;
		return true;
	}
	return false;
}

int IpcDevice::send(Message *msg, Message *outmsg)
{
	if( !opened )
		return false;

	size_t trueLen = msg->length();
	if( trueLen > SHARED_BUFFER_SIZE - SHARED_HEADER_SIZE)
		trueLen = SHARED_BUFFER_SIZE - SHARED_HEADER_SIZE;

	/* Copy the data into the buffer, let
		the opposite side handle */
	memcpy(lpMem, (void*)&trueLen, sizeof(int));

	long writeAddr = (long)lpMem + SHARED_HEADER_SIZE;
	memcpy((void*)writeAddr, msg->c_str(), trueLen);

	ReleaseMutex(memMutex);

	/* Wait for a response when it's finished */
    int waitsuccess = WaitForSingleObject(memMutex, 2000);
	if( waitsuccess != WAIT_OBJECT_0 )
	{
		printf("TIME OUT\n");
		return false;
	}

	/* Read header back to see if we have a message in return */
	unsigned long retMsgLen = 0;
	memcpy(&retMsgLen, lpMem, sizeof(int));
    memset(lpMem, 0, sizeof(int)); // Reset

	if( retMsgLen > 0)
	{
        /* Ensure that everything fits within the bounds of the buffer */
        if( retMsgLen > SHARED_BUFFER_SIZE - SHARED_HEADER_SIZE )
            retMsgLen = SHARED_BUFFER_SIZE - SHARED_HEADER_SIZE;

		outmsg->set_data((char*)writeAddr, retMsgLen);
	}
	else
	{
		outmsg->set_data("");
	}

	return true;
}
